/**
 * 
 */
package de.fsujena.chat_pi.server;

import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Observable;
import java.util.prefs.Preferences;

import de.fsujena.chat_pi.ressources.User.ROLE;
import de.fsujena.chat_pi.ressources.message.SysMessage;
import de.fsujena.chat_pi.ressources.message.TextMessage;
import de.fsujena.chat_pi.ressources.message.SysMessage.CATEGORY;
import de.fsujena.chat_pi.ressources.message.SysMessage.MESSAGE;
import de.fsujena.chat_pi.server.listener.NetworkListener;
import de.fsujena.chat_pi.server.manager.RoomManager;
import de.fsujena.chat_pi.server.manager.UserManager;
import de.fsujena.chat_pi.server.userInterface.*;

/**
 * Server implementation. It holds user and room manager and starts a network listener on 
 * a specific port. It also offers methods to display messages for the server.
 * 
 * @author Erik Hebestreit
 */
public class Server extends Observable {
	
	public UserManager userManager;
	public NetworkListener networkListener;
	public boolean isStarted=false;
	BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
	public Preferences prefs;
	public UserInterface ui;
	public RoomManager roomManager;
	public List<String> msgArray=new ArrayList<String>();

	/**
	 * @param args port can optionally be specified
	 * @throws IOException 
	 */
	public static void main(String[] args) {
		// create a new instance of the server object
		new Server().initServer(args);
	}
	
	/**
	 * Constructor. It launches the user manager and the network listener.
	 * 
	 * @param args arguments forwarded from main()
	 */
	public void initServer(String[] args) {
		// load preferences
		prefs = Preferences.userNodeForPackage(this.getClass());
		
		// initialize user and room manager
		userManager = new UserManager(this);
		roomManager = new RoomManager(this);

		// initialize user interface
		if (args.length>0 && args[0].equals("console")){
			ui = new ConsoleUI(this);
		} else {
			ui = new GraphicalUI(this);
		}
		new Thread(ui).start();
		
		
		
	}
	
	
	public void startNetworkListener() {
		try {
			// launch network listener on specific port
			networkListener = new NetworkListener(this.getPort(),this);
			
			/*
			 *  start network listener thread which handles login, registration and
			 *  listens for conventional messages
			 */
			networkListener.start();
			
			ui.postInfo(String.format("server started on port %d",this.getPort()));
		} catch (IOException e) {e.printStackTrace();}
	}
	
	/**
	 * Set the port to the preferences. Note that this setting only takes effect for a 
	 * newly created NetworkListener.
	 * 
	 * @param port port number where the server should listen
	 * @return <b>true</b> if port was set successfully,
	 * <b>false</b> if port number exceeded possible port numbers
	 */
	public boolean setPort(int port) {
		if (testPort(port)) {
			prefs.putInt("port", port);
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Get the currently set port. If no port was set the default value 3141 is returned.
	 * 
	 * @return port number where the server is listening
	 */
	public int getPort() {
		return prefs.getInt("port", 3141);
	}

	public boolean testPort(int num) {
		if (num > 1024 && num <= 65535)
			return true;
		else
			return false;
	}
	
	

	/**
	 * closes the server and cancels the connection
	 */
	public void stopServer(){
		SysMessage tempMsg=new SysMessage(CATEGORY.CONNECTION,MESSAGE.SERVER_CLOSED);
		tempMsg.setSenderUsername("SERVER");
		tempMsg.setSent(new Date());
		networkListener.distributeMsg(tempMsg);											//tell the clients that the server shuts down
		networkListener.close();														//removes connections
		networkListener=null;															//free resources
		ui.postInfo("server is closed");													//post info
		isStarted=false;

		super.setChanged();
		super.notifyObservers(isStarted);
		return;
	}

	/**
	 * starts the server
	 * 
	 * @param portstr String containing the port number
	 */
	public void startServer(){
		startNetworkListener();
		isStarted=true;

		super.setChanged();
		super.notifyObservers(isStarted);
		return;
	}

	/**
	 * closes the program
	 */
	public void exit(){
		if (isStarted)
			stopServer();					//at first cancel connection
		
		System.exit(0);						//stop the main thread
	}
	/**
	 * process the user input
	 * 
	 * @param command contains the user input
	 */
	public void commander(String command) {
		
		String[] arg = command.split(" "); 					//splits input on every " " (arg[0] should contain"$")

		if (arg[1].equals("start") && arg.length >= 3 && arg[2].equals("server")) {				//starts the server
			startServer();
		} else if (arg[1].equals("set") && arg.length >= 4 && arg[2].equals("port")) {
			setPort(Integer.parseInt(arg[3]));
		} else if (arg[1].equals("make") && arg.length >= 4 && arg[3].equals("admin")) {			//set userrights to admin
			userManager.setUserRole(arg[2], ROLE.ADMIN);
		} else if (arg[1].equals("make") && arg.length >= 4 && arg[3].equals("moderator")) {		//set userrights to moderator
			userManager.setUserRole(arg[2], ROLE.MODERATOR);
		} else if (arg[1].equals("make") && arg.length >= 4 && arg[3].equals("normal")) {			//set userrights to normal
			userManager.setUserRole(arg[2], ROLE.STANDARD);
		} else if (arg[1].equals("kick")&&arg[2].equals("user")) {									//kicks an user
			if(arg.length==4){
				userManager.kickUser(arg[3]);
			} else {
				ui.postError("invalid number of arguments");
			}
		} else if (arg[1].equals("bann")&&arg[2].equals("user")) {									//banns an user from the server
			if (arg.length==4) {
				userManager.bannUser(arg[3]);
			} else {
				ui.postError("invalid number of arguments");
			}
		} else if (arg[1].equals("unbann")&&arg[2].equals("user")) {								//unbann an user
			if (arg.length==4) {
				userManager.unbannUser(arg[3]);
			} else {
				ui.postError("invalid number of arguments");
			}
		} else if (arg[1].equals("remove")&&arg[2].equals("user")) {									//removes an user permanatly
			if (arg.length==4) {
				userManager.removeUser(arg[3]);
			} else {
				ui.postError("invalid number of arguments");
			}
		} else if (arg[1].equals("stop") && arg.length >= 3 && arg[2].equals("server")) {				//stops the server
			stopServer();
		} else if (arg[1].equals("exit")) {										//exit the programm
			exit();
		} else if ((arg[1].equals("warn"))&&(arg[2].equals("user"))) {
			if (arg.length>=4) {
				String temp="";
				for(int i=4;i<arg.length;i++){
					temp=temp+arg[i]+" ";
				}
				warnUser(arg[3],temp);
			} else {
				ui.postError("invalid number of arguments");
			}
		}else {
		
			ui.postError(("command not known"));
		}
	}
	
	public void warnUser(String user,String reason){
		
		if((userManager.checkUserExists(user))&&(networkListener.userExists(user)!=-1)){
			networkListener.messageListener.get(networkListener.userExists(user)).sendSysMessage(CATEGORY.MODERATE,MESSAGE.USER_WARNING,user,reason);
		}else {
			ui.postError("User doesn't exist or is not connected");
		}
	}
	
	
	/**
	 * save the history to a file
	 */
	public void saveHistory(){
		
		
		try {
			String relativpath=new File(".").getCanonicalPath();
			new File(relativpath+File.separatorChar+"history").mkdir();
			String filename=relativpath+File.separatorChar+"history"+File.separatorChar+this.getPort()+".dat";
			File file=new File(filename);
			FileOutputStream f;
			ObjectOutputStream s;
			f = new FileOutputStream(file);
			s = new ObjectOutputStream(f);
			s.writeObject(this.msgArray);
			s.close();
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
			
		} catch (IOException e) {
			e.printStackTrace();
			
		}
		
		
		
		return;
	}
	/**
	 * loads history from file
	 */
	@SuppressWarnings("unchecked")
	public void loadHistory(){
		
		try {
			String relativpath=new File(".").getCanonicalPath();
			String filename=relativpath+File.separatorChar+"history"+File.separatorChar+this.getPort()+".dat";
			File file=new File(filename);
			file.createNewFile();
			if(file.exists()){
				FileInputStream f = new FileInputStream(file);
			ObjectInputStream s = new ObjectInputStream(f);
			Object inputObject = s.readObject();
			
			if (inputObject instanceof List<?>)
				this.msgArray = (List<String>) inputObject;
			
			s.close();
			}
		} catch (IOException e) {
			//e.printStackTrace();
		} catch (ClassNotFoundException e) {
			//e.printStackTrace();
		}
		
		return;
	}
	
	public void showHistory(){
		if(msgArray!=null){
		loadHistory();
		for(int i=0;i<this.msgArray.size();i++){
			this.ui.postText(this.msgArray.get(i));
		}
		}
		}
	
	}

	

